using IQIGame.Onigao.Framework;

namespace IQIGame.Onigao.GamePlay
{
    public abstract class TEntity : TObject
    {
        private BetterSortedList<ETComponentType, TComponent> _components = new BetterSortedList<ETComponentType, TComponent>(4);
        protected BetterSortedList<ETComponentType, TComponent> componentMap
        {
            get { return this._components; }
        }

        public T AddComponent<T>(ETComponentType componentType) where T : TComponent, new()
        {
            if (!_components.TryGetValue(componentType, out var t))
            {
                t = new T();
                _components.Add(componentType, t);
                t.OnCreate(this);
            }
            return t as T;
        }

        public T AddComponent<T>(ETComponentType componentType, out bool isCreate) where T : TComponent, new()
        {
            if (!_components.TryGetValue(componentType, out var t))
            {
                t = new T();
                _components.Add(componentType, t);
                t.OnCreate(this);
                isCreate = true;
            }
            else
            {
                isCreate = false;
            }
            return t as T;
        }

        public T AddComponentWithPool<T>(ETComponentType componentType) where T : TComponent, IReusableClass, new()
        {
            if (!_components.TryGetValue(componentType, out var t))
            {
                t = ClassPool.Get<T>();
                _components.Add(componentType, t);
                t.OnCreate(this);
            }
            return t as T;
        }

        public T AddComponentWithPool<T>(ETComponentType componentType, out bool isCreate) where T : TComponent, IReusableClass, new()
        {
            if (!_components.TryGetValue(componentType, out var t))
            {
                t = ClassPool.Get<T>();
                _components.Add(componentType, t);
                t.OnCreate(this);
                isCreate = true;
            }
            else
            {
                isCreate = false;
            }
            return t as T;
        }

        public T AddComponent<T>(T component, ETComponentType componentType) where T : TComponent, new()
        {
            component.OnCreate(this);
            if (_components.ContainsKey(componentType))
            {
                _components.Remove(componentType);
            }
            _components.Add(componentType, component);
            return component;
        }

        public T GetComponent<T>(ETComponentType componentType) where T : TComponent
        {
            if (_components.TryGetValue(componentType, out var t))
            {
                return t as T;
            }
            return default(T);
        }

        public bool Exists<T>(ETComponentType componentType) where T : TComponent
        {
            return _components.ContainsKey(componentType);
        }

        public void DeleteComponent(ETComponentType componentType)
        {
            if (_components.TryGetValue(componentType, out var t))
            {
                if (t is IReusableClass reusableClass)
                {
                    ClassPool.PutAutoType(reusableClass);
                }
                else
                {
                    t.Dispose();
                }
                _components.Remove(componentType);
            }
        }

        public virtual void OnUpdate(float deltaTime)
        {
            foreach (var kv in componentMap)
            {
                kv.Value.OnUpdate(deltaTime);
            }
        }

        public override void Dispose()
        {
            DisposeComponents();
            base.Dispose();
        }

        protected void DisposeComponents()
        {
            foreach (var kv in _components)
            {
                var component = kv.Value;
                try
                {
                    if (component is IReusableClass reusableClass)
                    {
                        ClassPool.PutAutoType(reusableClass);
                    }
                    else
                    {
                        component.Dispose();
                    }
                }
                catch (System.Exception e)
                {
                    LogGame.LogException(e);
                }
            }
            _components.Clear();
        }

    }
}
